package com.wrh.springboot.restwrap;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wrh.springboot.restwrap.propertyeditor.CustomLocalDateTimeEditor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.LocalDateTime;

/**
 * @author wrh
 */
@Slf4j
@ControllerAdvice(annotations = {RestController.class, Controller.class})
public class RestResultAdvice implements ResponseBodyAdvice<Object> {

    private final ObjectMapper objectMapper;
    private final RestResultConfig restResultConfig;

    public RestResultAdvice(ObjectMapper objectMapper, RestResultConfig restResultConfig) {
        this.objectMapper = objectMapper;
        this.restResultConfig = restResultConfig;
    }

    /**
     * 这个方法表示对于哪些请求要执行beforeBodyWrite，返回true执行，返回false不执行
     */
    @Override
    public boolean supports(@Nullable MethodParameter methodParameter, @Nullable Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }


    @Override
    public Object beforeBodyWrite(Object body, @Nullable MethodParameter methodParameter, @Nullable MediaType mediaType, @Nullable Class<? extends HttpMessageConverter<?>> aClass, @Nullable ServerHttpRequest serverHttpRequest, @Nullable ServerHttpResponse serverHttpResponse) {
        if (serverHttpRequest != null) {
            HttpServletRequest httpServletRequest = ((ServletServerHttpRequest) serverHttpRequest).getServletRequest();
            if (httpServletRequest.getAttribute(FinalString.INVOKE_REST_WRAP) != null) {
                Long invokeStartTimestamp = (Long) httpServletRequest.getAttribute(FinalString.INVOKE_START_TIMESTAMP);
                Long cost = System.currentTimeMillis() - invokeStartTimestamp;
                httpServletRequest.removeAttribute(FinalString.INVOKE_REST_WRAP);
                httpServletRequest.removeAttribute(FinalString.INVOKE_START_TIMESTAMP);

                if (restResultConfig.isResponseInfoEnabled()) {
                    if (mediaType != null) {
                        log.info("CONTENT_TYPE : {}", mediaType.toString());
                    }
                    try {
                        String data = objectMapper.writeValueAsString(body);
                        if (data.length() > 100) {
                            data = data.substring(0, 100) + " ... ";
                        }
                        log.info("RESPONSE_BODY: {}", data);
                    } catch (JsonProcessingException e) {
                        log.info("RESPONSE_BODY: JsonProcessingException -> {}", e.getMessage());
                    }
                }


                if (body instanceof ResponseEntity) {
                    return body;
                }
                if (body instanceof RestResult) {
                    return body;
                }

                if (MediaType.APPLICATION_JSON.equals(mediaType) || MediaType.TEXT_PLAIN.equals(mediaType)) {
                    return RestResponse.ok(cost, body);
                } else {
                    return body;
                }
            }
        }
        return body;
    }


    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(LocalDateTime.class, new CustomLocalDateTimeEditor("yyyy-MM-dd HH:mm"));
    }


    /**
     * 全局异常处理
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public Object handleException(Exception e, HttpServletRequest httpServletRequest) {
        log.debug("ExceptionHandler 全局异常处理");
        log.error(e.getMessage(), e);
        if (httpServletRequest.getAttribute(FinalString.REST_WRAP_IGNORE) != null) {
            httpServletRequest.removeAttribute(FinalString.REST_WRAP_IGNORE);
            StringWriter sw = new StringWriter();
            e.printStackTrace(new PrintWriter(sw, true));
            return sw.toString();
        } else {
            return RestResponse.err(e);
        }
    }

}
